package com.easylinkin.linkappapi.deviceattributestatus.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.common.utils.CommonUtils;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.config.entity.Config;
import com.easylinkin.linkappapi.config.service.ConfigService;
import com.easylinkin.linkappapi.device.constant.DeviceConstant.ModelCategoryEnum;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.entity.DeviceModel;
import com.easylinkin.linkappapi.device.entity.vo.*;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.device.mapper.DeviceModelMapper;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.device.service.ElectricyRecordsService;
import com.easylinkin.linkappapi.device.service.SprayRecordsService;
import com.easylinkin.linkappapi.device.service.WaterRecordsService;
import com.easylinkin.linkappapi.deviceattribute.constant.PhysicalModelTypeConstant;
import com.easylinkin.linkappapi.deviceattribute.entity.DeviceAttribute;
import com.easylinkin.linkappapi.deviceattribute.mapper.DeviceAttributeMapper;
import com.easylinkin.linkappapi.deviceattribute.service.DeviceAttributeService;
import com.easylinkin.linkappapi.deviceattributestatus.entity.DeviceAttributeStatus;
import com.easylinkin.linkappapi.deviceattributestatus.mapper.DeviceAttributeStatusMapper;
import com.easylinkin.linkappapi.deviceattributestatus.service.DeviceAttributeStatusService;
import com.easylinkin.linkappapi.devicetype.entity.DeviceType;
import com.easylinkin.linkappapi.devicetype.service.DeviceTypeService;
import com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit;
import com.easylinkin.linkappapi.deviceunit.service.DeviceUnitService;
import com.easylinkin.linkappapi.elasticsearch.service.IEService;
import com.easylinkin.linkappapi.iaqi.config.IaqiConfigUtil;
import com.easylinkin.linkappapi.inspection.entity.ElectricBox;
import com.easylinkin.linkappapi.inspection.service.ElectricBoxService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.mapper.LinkappTenantMapper;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.easylinkin.linkappapi.device.constant.DeviceConstant.ModelCategoryEnum.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author TongJie
 * @since 2020-04-18
 */
@Service
public class DeviceAttributeStatusServiceImpl extends ServiceImpl<DeviceAttributeStatusMapper, DeviceAttributeStatus> implements DeviceAttributeStatusService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceAttributeStatusServiceImpl.class);

    @Resource
    LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private DeviceModelMapper deviceModelMapper;

    @Resource
    IEService realService;
    @Resource
    LinkappTenantService linkappTenantService;
    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private DeviceAttributeMapper deviceAttributeMapper;
    @Resource
    private DeviceAttributeService deviceAttributeService;
    @Resource
    private DeviceService deviceService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ElectricBoxService electricBoxService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private DeviceTypeService deviceTypeService;

    @Resource
    private IaqiConfigUtil iaqiConfigUtil;

    @Resource
    private LinkappTenantMapper linkappTenantMapper;

    @Override
    public IPage<DeviceAttributeStatus> getPageGlobal(Page page, DeviceAttributeStatus deviceAttributeStatus) {
        return baseMapper.getPageGlobal(page, deviceAttributeStatus);
    }

    @Override
    public List<DeviceAttributeStatus> getPageGlobal(DeviceAttributeStatus deviceAttributeStatus) {
        List<DeviceAttributeStatus> attributeStatusList = baseMapper.getPageGlobal(deviceAttributeStatus);
        return attributeStatusList;
    }

    @Override
    public List<DeviceAttributeStatus> getProps(DeviceAttributeStatus deviceAttributeStatus) {
        QueryWrapper<DeviceAttributeStatus> queryWrapper = new QueryWrapper<>();
        //获取当前设备在当前项目下的版本
        Device device = new Device();
        device.setCode(deviceAttributeStatus.getDeviceCode());
        List<Device> devices = deviceService.selectDevices(device);
        if (CollectionUtil.isEmpty( devices)){
            return null;
        }
        if (StringUtils.isNotEmpty(deviceAttributeStatus.getDeviceCode())) {
            queryWrapper.eq("version", devices.get(0).getDeviceUnitVersion());
            queryWrapper.eq("device_code", deviceAttributeStatus.getDeviceCode());
        }
        queryWrapper.select("distinct  prop_code", "prop_name", "prop_unit");
        queryWrapper.orderByAsc("prop_code");
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public void exportData(DeviceAttributeStatus deviceAttributeStatus, HttpServletRequest request, HttpServletResponse response) {
        List<DeviceAttributeStatus> result = getPageGlobal(deviceAttributeStatus);
        HashMap<String, String> keyMap = new HashMap<>(10);

        List<HashMap> resultList = new ArrayList<>();
        HashMap<String, Object> resultMap;
        for (DeviceAttributeStatus attributeStatus : result) {
            resultMap = new HashMap<>();
            for (DeviceAttributeStatus status : attributeStatus.getChildAttributeList()) {
                keyMap.put(status.getPropName(), status.getPropCode());
                resultMap.put(status.getPropCode(), status.getPropValue());
            }
            resultMap.put("modifyTime", attributeStatus.getModifyTime());
            resultList.add(resultMap);
        }
        keyMap.put("修改时间", "modifyTime");
        StringBuilder stringBuilder = new StringBuilder();
        for (String key : keyMap.keySet()) {
            stringBuilder.append(key).append(":").append(keyMap.get(key)).append(",");
        }
        stringBuilder.delete(stringBuilder.length() - 1, stringBuilder.length());
        List<HashMap<String, Object>> resultSimple = null;
        String keyValue = stringBuilder.toString();

        String title = "设备属性状态";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, resultList, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }

    }

    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeData(DeviceAttributeStatus deviceAttributeStatus) {
        LinkappUser linkappUser = null;
        if (StringUtils.isEmpty(deviceAttributeStatus.getTenantId())) {
            linkappUser = linkappUserContextProducer.getNotNullCurrent();
            deviceAttributeStatus.setTenantId(linkappUser.getTenantId());
        } else {
            linkappUser = new LinkappUser();
            linkappUser.setTenantId(deviceAttributeStatus.getTenantId());
        }
        List<DeviceAttributeStatus> deviceAttributes = getDeviceRealtimeDataGlobal(deviceAttributeStatus);
        //    格式化 设备属性列表，按照约定结构返回

        //   去除 查得的子属性有 对应的父属性却没在 集合中的。如果属性有租户个性化的且复杂类型的父属性isShow 是false 会发生此现象
        Set<String> propCodes = deviceAttributes.stream().map(e -> e.getPropCode()).collect(Collectors.toSet());
        List<DeviceAttributeStatus> deviceAttributeStatusList = new ArrayList<>();
        for (DeviceAttributeStatus deviceAttribute : deviceAttributes) {
            if (ObjectUtils.isNotEmpty(deviceAttribute.getParentPropCode()) && !propCodes.contains(deviceAttribute.getParentPropCode())) {
                continue;
            }
            deviceAttributeStatusList.add(deviceAttribute);
        }
        List<DeviceAttributeStatus> formatDeviceAttributeStatusList = getFormatDeviceAttributeStatusList(deviceAttributeStatusList);
        //20221011增加没有设备编码、无数据也展示属性：只在设备上报数据为空的时候走新增逻辑
        Boolean noDataShow = deviceAttributeStatus.getNoDataShow();
        if(noDataShow != null && noDataShow){
            if(CollectionUtil.isEmpty(formatDeviceAttributeStatusList)){
                formatDeviceAttributeStatusList = new ArrayList<>();
            }
            //TODO
            noDataShowAttribute(deviceAttributeStatus,formatDeviceAttributeStatusList,linkappUser);
        }

        return formatDeviceAttributeStatusList;
    }

    private void noDataShowAttribute(DeviceAttributeStatus deviceAttributeStatus, List<DeviceAttributeStatus> formatDeviceAttributeStatusList, LinkappUser linkappUser) {

        String typeCode = deviceAttributeStatus.getTypeCode();
        //根据typeCode找名称(app_machinery_type)->根据名称找设备分类（linkapp_device_type）->根据分类id、版本找型号（linkapp_device_unit）->根据型号找属性
        if(StringUtils.isNotBlank(typeCode)){
            String tenantId = linkappUser.getTenantId();
            //来源设备电子档案
            String machineryTypeRefConfigStartKey = "MACHINERY_TYPE_";
            String deviceUnitCodeConfigKey = machineryTypeRefConfigStartKey + "" + typeCode;

            Config config = iaqiConfigUtil.getOneConfigs(deviceUnitCodeConfigKey, tenantId);
            if(config != null){
                AtomicReference<String> typeName = new AtomicReference<>("");
                JSONArray jsonArray = JSONUtil.parseArray(config.getValue());
                jsonArray.stream().forEach(p -> {
                    cn.hutool.json.JSONObject jsonObject = (cn.hutool.json.JSONObject) p;
                    if (jsonObject.containsKey("iotType") && jsonObject.containsKey("unitTypeName")) {
                        String iotType = jsonObject.getStr("iotType");
                        if ("main".equals(iotType)) {
                            typeName.set(jsonObject.getStr("unitTypeName"));
                        }
                    }
                });
                if(StringUtils.isNotBlank(typeName.get())){
                    //懒得写关联，直接一级级找吧，也许父级还有用
                    DeviceType deviceTypeExp = new DeviceType();
                    deviceTypeExp.setDeleteState(1);
                    deviceTypeExp.setName(typeName.get());
                    List<DeviceType> deviceTypeList = deviceTypeService.getAll(deviceTypeExp);
                    if(CollectionUtil.isNotEmpty(deviceTypeList)){
                        DeviceType deviceType = deviceTypeList.get(0);
                        DeviceUnit deviceUnitEx = new DeviceUnit();
                        deviceUnitEx.setDeleteState(1);
                        deviceUnitEx.setDeviceTypeId(deviceType.getId());
                        List<DeviceUnit> deviceUnitList = deviceUnitService.getAll(deviceUnitEx);
                        if(CollectionUtil.isNotEmpty(deviceUnitList)){
                            DeviceUnit maxVersionUnit = deviceUnitList.stream().max(Comparator.comparing(DeviceUnit::getVersion)).get();
                            DeviceAttribute attrEx = new DeviceAttribute();
                            attrEx.setDeviceUnitId(maxVersionUnit.getId());

                            QueryWrapper<DeviceAttribute> qw = new QueryWrapper<>();
                            qw.eq("device_unit_id",maxVersionUnit.getId());
                            if(deviceAttributeStatus.getIsShow() != null || deviceAttributeStatus.getIsShow()){
                                qw.eq("is_show",deviceAttributeStatus.getIsShow());
                            }

                            List<DeviceAttribute> attrLs = deviceAttributeMapper.selectList(qw);
                            //已存在的属性则不进行填充
                            List<String> existProp = formatDeviceAttributeStatusList.stream().map(DeviceAttributeStatus::getPropCode).collect(Collectors.toList());
                            if(CollectionUtil.isNotEmpty(attrLs)){
                                attrLs.stream().forEach(t -> {
                                    if (!existProp.contains(t.getIdentifier())) {
                                        DeviceAttributeStatus attrStatus = convertAttrToAttrStatus(t);
                                        formatDeviceAttributeStatusList.add(attrStatus);
                                    }
                                });
                            }
                        }
                    }
                }
            }

        }
    }

    private DeviceAttributeStatus convertAttrToAttrStatus(DeviceAttribute t) {
        DeviceAttributeStatus deviceAttributeStatus = new DeviceAttributeStatus();
        deviceAttributeStatus.setId(t.getId());
        deviceAttributeStatus.setPropName(t.getName());
        deviceAttributeStatus.setPropCode(t.getIdentifier());
        deviceAttributeStatus.setPropUnit(t.getUnit());
        deviceAttributeStatus.setUnit(t.getUnit());
        deviceAttributeStatus.setVersion(t.getVersion());
        deviceAttributeStatus.setParentId(t.getParentId());
        deviceAttributeStatus.setArrayIndex(t.getArrayIndex());
        deviceAttributeStatus.setParentPropCode(t.getParentPropCode());
        deviceAttributeStatus.setPropValue(t.getPropValue());
        deviceAttributeStatus.setDeviceUnitId(t.getDeviceUnitId());
        deviceAttributeStatus.setDeviceUnitCode(t.getDeviceUnitCode());
        deviceAttributeStatus.setSortNo(t.getSortNo());
        deviceAttributeStatus.setIsShow(t.getIsShow());
        deviceAttributeStatus.setSpecs(t.getSpecs());
        deviceAttributeStatus.setVisualizationConfig(t.getVisualizationConfig());
        deviceAttributeStatus.setTenantId(t.getTenantId());
        deviceAttributeStatus.setIcoPath(t.getIcoPath());
        return deviceAttributeStatus;
    }

    @Autowired
    private DeviceUnitService deviceUnitService;

    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeDataForBigScreen(Device device) {
        //判断是否是配电箱，如果是配电箱，需要找到对应的绑定设备的属性
        if (device.getModelId().equals(-1)) {
            if ("配电箱".equals(device.getDeviceTypeName())){
                ElectricBox electricBox = electricBoxService.getById(device.getId());
                if (electricBox != null && StringUtils.isNotBlank(electricBox.getMonitorDeviceCode())) {
                    Config electricboxShowAtrrtConfig = configService
                        .getOneByKey("ELECTRICBOX_REF_DEVICE_SHOW_ATTR");
                    List<String> attrs = null;
                    if (electricboxShowAtrrtConfig != null) {
                        String value = electricboxShowAtrrtConfig.getValue();
                        //暂时考虑可能将来会有多种类型，这里做个将来的扩展，用逗号隔开
                        if (StringUtils.isNotBlank(value)) {
                            String[] split = value.split(",");
                            attrs = Arrays.asList(split);
                        }
                    }
                    return getArrtibuteStatusByCode(electricBox.getMonitorDeviceCode(), attrs);
                }
            }else if("卸料平台".equals(device.getDeviceTypeName())){
                List<String> attrs = new ArrayList<>();
                attrs.add("rated_load");
                attrs.add("max_tilt_angle");
                attrs.add("height");
                attrs.add("height_percent");
                attrs.add("angle_x");
                attrs.add("angle_x_percent");
                attrs.add("angle_y");
                attrs.add("angle_y_percent");
                return getArrtibuteStatusByCode(device.getCode(), attrs);
            }else {
                return getArrtibuteStatusByCode(device.getCode(), null);
            }
        }else if (device.getModelId().equals(0)) {
            //如果是0，则需要进行判断是哪种设备然后进行取不同的值
            Device exist = deviceService.findOneByDeviceCode(device.getCode());
            //获取对应的型号
            DeviceUnit deviceUnit = deviceUnitService.getById(exist.getDeviceUnitId());
            Config sparyDeviceTypeNames = configService.getOneByKey(SPARY.getDeviceTypeNameKey());//喷淋
            Config waterDeviceTypeNames = configService.getOneByKey(WATTER.getDeviceTypeNameKey());//水表
            Config raiseDustDeviceTypeNames = configService
                .getOneByKey(RAISE_DUST.getDeviceTypeNameKey());//扬尘
            Config electricyDustDeviceTypeNames = configService
                .getOneByKey(ELECTRICY.getDeviceTypeNameKey());//电表
            Config showAttrTypeNames = configService.getOneByKey("SHOW_ATTR_TYPE_NAMES");
            if(showAttrTypeNames!=null){
                String showAttrTypeNamesStr = showAttrTypeNames.getValue();
                if (StringUtils.isNotBlank(showAttrTypeNamesStr)) {
                    String[] split = showAttrTypeNamesStr.split(",");
                    List<String> showAttrTypeNamesCodes = Arrays.asList(split);
                    if (showAttrTypeNamesCodes.contains(deviceUnit.getDeviceTypeName())) {
                        Config electricboxShowAtrrtConfig = configService
                            .getOneByKey("ELECTRICBOX_REF_DEVICE_SHOW_ATTR");
                        List<String> attrs = null;
                        if (electricboxShowAtrrtConfig != null) {
                            String value = electricboxShowAtrrtConfig.getValue();
                            //暂时考虑可能将来会有多种类型，这里做个将来的扩展，用逗号隔开
                            if (StringUtils.isNotBlank(value)) {
                                String[] split1 = value.split(",");
                                attrs = Arrays.asList(split1);
                            }
                        }
                        return getArrtibuteStatusByCode(device.getCode(), attrs);
                    }
                }
            }
            if(sparyDeviceTypeNames!=null){
                String sparyDeviceUnitCodesStr = sparyDeviceTypeNames.getValue();
                if (StringUtils.isNotBlank(sparyDeviceUnitCodesStr)) {
                    String[] split = sparyDeviceUnitCodesStr.split(",");
                    List<String> sparyDeviceUnitCodes = Arrays.asList(split);
                    if (sparyDeviceUnitCodes.contains(deviceUnit.getDeviceTypeName())) {
                        return getAttributeByCodeAndType(device.getCode(), SPARY);
                    }
                }
            }
            if(waterDeviceTypeNames!=null){
                String waterDeviceUnitCodesStr = waterDeviceTypeNames.getValue();
                if (StringUtils.isNotBlank(waterDeviceUnitCodesStr)) {
                    String[] split = waterDeviceUnitCodesStr.split(",");
                    List<String> waterDeviceUnitCodes = Arrays.asList(split);
                    if (waterDeviceUnitCodes.contains(deviceUnit.getDeviceTypeName())) {
                        return getAttributeByCodeAndType(device.getCode(), WATTER);
                    }
                }
            }
            if(raiseDustDeviceTypeNames!=null){
                String raiseDustDeviceUnitCodesStr = raiseDustDeviceTypeNames.getValue();
                if (StringUtils.isNotBlank(raiseDustDeviceUnitCodesStr)) {
                    String[] split = raiseDustDeviceUnitCodesStr.split(",");
                    List<String> raiseDustDeviceUnitCodes = Arrays.asList(split);
                    if (raiseDustDeviceUnitCodes.contains(deviceUnit.getDeviceTypeName())) {
                        return getAttributeByCodeAndType(device.getCode(), RAISE_DUST);
                    }
                }
            }
            if(electricyDustDeviceTypeNames!=null){
                String electricyDustDeviceUnitCodesStr = electricyDustDeviceTypeNames.getValue();
                if (StringUtils.isNotBlank(electricyDustDeviceUnitCodesStr)) {
                    String[] split = electricyDustDeviceUnitCodesStr.split(",");
                    List<String> electricyDustDeviceUnitCodes = Arrays.asList(split);
                    if (electricyDustDeviceUnitCodes.contains(deviceUnit.getDeviceTypeName())) {
                        return getAttributeByCodeAndType(device.getCode(), ELECTRICY);
                    }
                }
            }
            return getArrtibuteStatusByCode(device.getCode(), null);
        }
        return null;
    }

    @Resource
    private SprayRecordsService appSprayRecordsService;

    @Resource
    private WaterRecordsService appWaterRecordsService;

    @Resource
    private ElectricyRecordsService electricyRecordsService;

    /**
     * 根据code和类型进行查询相关的属性
     * @param code
     * @param modelCategory
     * @return
     */
    private List<DeviceAttributeStatus> getAttributeByCodeAndType(String code, ModelCategoryEnum modelCategory) {
        switch (modelCategory){
            //喷淋
            case SPARY:
                SprayRecordsVo appSprayRecords = new SprayRecordsVo();
                appSprayRecords.setDeviceCode(code);
                SprayStatisticsVo sprayStatistics = appSprayRecordsService
                    .getSprayStatistics(appSprayRecords);
                List<DeviceAttributeStatus> attributeStatuses = new ArrayList<>();
                if(sprayStatistics!=null){
                    Double dayAvgSprayHours = sprayStatistics.getDayAvgSprayHours();//日均喷淋时长
                    DeviceAttributeStatus status = new DeviceAttributeStatus();
                    status.setDeviceCode(code);
                    status.setPropCode("dayAvgSprayHours");
                    status.setPropName("日均喷淋时长");
                    if(dayAvgSprayHours!=null){
                        status.setPropValue(String.format("%.2f", dayAvgSprayHours));
                    }else {
                        status.setPropValue(null);
                    }
                    JSONObject specs = new JSONObject();
                    specs.put("unit","h");
                    status.setSpecs(specs.toJSONString());
                    attributeStatuses.add(status);

//                    Double todaySprayHours = sprayStatistics.getTodaySprayHours();//今日喷淋时长
//                    DeviceAttributeStatus todaySprayHoursStatus = new DeviceAttributeStatus();
//                    todaySprayHoursStatus.setDeviceCode(code);
//                    todaySprayHoursStatus.setPropCode("todaySprayHours");
//                    todaySprayHoursStatus.setPropName("今日喷淋时长");
//                    if(todaySprayHours!=null){
//                        todaySprayHoursStatus.setPropValue(String.valueOf(todaySprayHours));
//                    }else {
//                        todaySprayHoursStatus.setPropValue(null);
//                    }
//                    todaySprayHoursStatus.setSpecs(specs.toJSONString());
//                    attributeStatuses.add(todaySprayHoursStatus);

                    Double totalSprayHours = sprayStatistics.getTotalSprayHours();//累计喷淋时长
                    DeviceAttributeStatus todtalSprayHoursStatus = new DeviceAttributeStatus();
                    todtalSprayHoursStatus.setDeviceCode(code);
                    todtalSprayHoursStatus.setPropCode("totalSprayHours");
                    todtalSprayHoursStatus.setPropName("累计喷淋时长");
                    if(totalSprayHours!=null){
                        todtalSprayHoursStatus.setPropValue(String.format("%.2f", totalSprayHours));
                    }else {
                        todtalSprayHoursStatus.setPropValue(null);
                    }
                    todtalSprayHoursStatus.setSpecs(specs.toJSONString());
                    attributeStatuses.add(todtalSprayHoursStatus);

                    Integer sprayStatus = sprayStatistics.getSprayStatus();//喷淋状态
                    DeviceAttributeStatus sprayStatusStatus = new DeviceAttributeStatus();
                    sprayStatusStatus.setDeviceCode(code);
                    sprayStatusStatus.setPropCode("sprayStatus");
                    sprayStatusStatus.setPropName("喷淋状态");
                    if(sprayStatus!=null){
                        //0:断开1：闭合
                        sprayStatusStatus.setPropValue(String.valueOf(sprayStatus));
                        if(sprayStatus.equals(0)){
                            sprayStatusStatus.setPropValue("关闭");
                        }else if(sprayStatus.equals(1)) {
                            sprayStatusStatus.setPropValue("开启");
                        }
                    }else {
                        sprayStatusStatus.setPropValue(null);
                    }
                    attributeStatuses.add(sprayStatusStatus);

                }
                return attributeStatuses;
            case WATTER:
                WaterRecordsVo appWaterRecords = new WaterRecordsVo();
                appWaterRecords.setDeviceCode(code);
                WaterStatisticsVo waterStatistics = appWaterRecordsService
                    .getWaterStatistics(appWaterRecords);
                List<DeviceAttributeStatus> waterAttributeStatuses = new ArrayList<>();
                Double todayWater = waterStatistics.getTodayWater();
                DeviceAttributeStatus status = new DeviceAttributeStatus();
                status.setDeviceCode(code);
                status.setPropCode("todayWater");
                status.setPropName("今日用水量");
                if(todayWater!=null){
                    status.setPropValue(String.valueOf(todayWater));
                }else {
                    status.setPropValue(null);
                }
                JSONObject specs = new JSONObject();
                specs.put("unit","t");
                status.setSpecs(specs.toJSONString());
                waterAttributeStatuses.add(status);

                Double weekWater = waterStatistics.getWeekWater();
                DeviceAttributeStatus weekStatus = new DeviceAttributeStatus();
                weekStatus.setDeviceCode(code);
                weekStatus.setPropCode("weekWater");
                weekStatus.setPropName("本周用水量");
                if(weekWater!=null){
                    weekStatus.setPropValue(String.valueOf(weekWater));
                }else {
                    weekStatus.setPropValue(null);
                }
                weekStatus.setSpecs(specs.toJSONString());
                waterAttributeStatuses.add(weekStatus);

                Double monthWater = waterStatistics.getMonthWater();
                DeviceAttributeStatus monthStatus = new DeviceAttributeStatus();
                monthStatus.setDeviceCode(code);
                monthStatus.setPropCode("monthWater");
                monthStatus.setPropName("本月用水量");
                if(monthWater!=null){
                    monthStatus.setPropValue(String.valueOf(monthWater));
                }else {
                    monthStatus.setPropValue(null);
                }
                monthStatus.setSpecs(specs.toJSONString());
                waterAttributeStatuses.add(monthStatus);

                Double totalWater = waterStatistics.getTotalWater();
                DeviceAttributeStatus totalStatus = new DeviceAttributeStatus();
                totalStatus.setDeviceCode(code);
                totalStatus.setPropCode("totalWater");
                totalStatus.setPropName("总用水量");
                if(totalWater!=null){
                    totalStatus.setPropValue(String.valueOf(totalWater));
                }else {
                    totalStatus.setPropValue(null);
                }
                totalStatus.setSpecs(specs.toJSONString());
                waterAttributeStatuses.add(totalStatus);
                return waterAttributeStatuses;
            case RAISE_DUST:
                //获取扬尘展示配置
                Config electricboxShowAtrrtConfig = configService
                    .getOneByKey("RAISE_DUST_DEVICE_SHOW_ATTR");
                List<String> attrs = null;
                if (electricboxShowAtrrtConfig != null) {
                    String value = electricboxShowAtrrtConfig.getValue();
                    //暂时考虑可能将来会有多种类型，这里做个将来的扩展，用逗号隔开
                    if (StringUtils.isNotBlank(value)) {
                        String[] split = value.split(",");
                        attrs = Arrays.asList(split);
                    }
                }
                return getArrtibuteStatusByCode(code, attrs);
            case ELECTRICY:
                ElectricyRecordsVo electricyRecordsVo = new ElectricyRecordsVo();
                electricyRecordsVo.setDeviceCode(code);
                ElectricyStatisticsVo electricyStatistics = electricyRecordsService
                    .getElectricyStatistics(electricyRecordsVo);
                List<DeviceAttributeStatus> electricyStatisticsStatus = new ArrayList<>();

                Double todayElectricy = electricyStatistics.getTodayElectricy();
                DeviceAttributeStatus todayElectricyStatus = new DeviceAttributeStatus();
                todayElectricyStatus.setDeviceCode(code);
                todayElectricyStatus.setPropCode("todayElectricy");
                todayElectricyStatus.setPropName("今日用电量");
                if(todayElectricy!=null){
                    todayElectricyStatus.setPropValue(String.valueOf(todayElectricy));
                }else {
                    todayElectricyStatus.setPropValue(null);
                }
                specs = new JSONObject();
                specs.put("unit","kW·h");
                todayElectricyStatus.setSpecs(specs.toJSONString());
                electricyStatisticsStatus.add(todayElectricyStatus);

                Double weekElectricy = electricyStatistics.getWeekElectricy();
                DeviceAttributeStatus weekElectricyStatus = new DeviceAttributeStatus();
                weekElectricyStatus.setDeviceCode(code);
                weekElectricyStatus.setPropCode("weekElectricy");
                weekElectricyStatus.setPropName("本周用电量");
                if(weekElectricy!=null){
                    weekElectricyStatus.setPropValue(String.valueOf(weekElectricy));
                }else {
                    weekElectricyStatus.setPropValue(null);
                }
                weekElectricyStatus.setSpecs(specs.toJSONString());
                electricyStatisticsStatus.add(weekElectricyStatus);

                Double monthElectricy = electricyStatistics.getMonthElectricy();
                DeviceAttributeStatus monthElectricyStatus = new DeviceAttributeStatus();
                monthElectricyStatus.setDeviceCode(code);
                monthElectricyStatus.setPropCode("monthElectricy");
                monthElectricyStatus.setPropName("本月用电量");
                if(monthElectricy!=null){
                    monthElectricyStatus.setPropValue(String.valueOf(monthElectricy));
                }else {
                    monthElectricyStatus.setPropValue(null);
                }
                monthElectricyStatus.setSpecs(specs.toJSONString());
                electricyStatisticsStatus.add(monthElectricyStatus);

                Double totalElectricy = electricyStatistics.getTotalElectricy();
                DeviceAttributeStatus totalElectricyStatus = new DeviceAttributeStatus();
                totalElectricyStatus.setDeviceCode(code);
                totalElectricyStatus.setPropCode("totalElectricy");
                totalElectricyStatus.setPropName("总用电量");
                if(totalElectricy!=null){
                    totalElectricyStatus.setPropValue(String.valueOf(totalElectricy));
                }else {
                    totalElectricyStatus.setPropValue(null);
                }
                totalElectricyStatus.setSpecs(specs.toJSONString());
                electricyStatisticsStatus.add(totalElectricyStatus);

                return electricyStatisticsStatus;

        }
        return null;
    }

    /**
     * 通过设备code获取最新属性值的方法
     * @param code
     * @param attrs
     * @return
     */
    private List<DeviceAttributeStatus> getArrtibuteStatusByCode(String code,
        List<String> attrs) {
        Device exist = deviceService.findOneByDeviceCode(code);
        if(exist==null){
            return null;
        }
        //找到对应的deviceUnitId的版本号
        Device param = new Device();
        param.setId(exist.getId());
        List<Device> devices = deviceService.selectDevices(param);
        //这里应该只有一条
        if(CollectionUtil.isNotEmpty(devices)){
            Device device = devices.get(0);
            DeviceUnit deviceUnit = deviceUnitService.getById(device.getDeviceUnitId());
            DeviceAttributeStatus status = new DeviceAttributeStatus();
            status.setDeviceCode(device.getCode());
            if(deviceUnit!=null){
                status.setVersion(deviceUnit.getVersion());
            }
            List<DeviceAttributeStatus> deviceRealtimeDataByDevice = getDeviceRealtimeData(status);
//            List<DeviceAttributeStatus> deviceRealtimeDataByDevice = getDeviceRealtimeDataByDevice(
//                device);
            if(CollectionUtil.isNotEmpty(attrs)){
                List<DeviceAttributeStatus> collect = deviceRealtimeDataByDevice.stream()
                    .filter(item -> {
                        return attrs.contains(item.getPropCode());
                    }).collect(Collectors.toList());
                // active_power 有功总功率; temperature_1 通道1温度 ;temperature_2 temperature_3 temperature_4  residual_current
                Map<String,String> keyMap = new HashMap<>();
                //配电箱的属性集合
                keyMap.put("active_power","有功总功率");
                keyMap.put("temperature_1","通道1温度");
                keyMap.put("temperature_2","通道2温度");
                keyMap.put("temperature_3","通道3温度");
                keyMap.put("temperature_4","通道4温度");
                keyMap.put("residual_current","漏电流");

                //卸料平台的属性
                keyMap.put("rated_load","额定载重");
                keyMap.put("max_tilt_angle","最大倾斜角度");
                keyMap.put("height","实时载重");
                keyMap.put("height_percent","重量百分比");
                keyMap.put("angle_x","倾角X");
                keyMap.put("angle_x_percent","倾角X百分比");
                keyMap.put("angle_y","倾角Y");
                keyMap.put("angle_y_percent","倾角Y百分比");
                List<String> propCodes = collect.stream().map(item -> item.getPropCode())
                    .collect(Collectors.toList());
                attrs.forEach(item->{
                    if(!propCodes.contains(item)){
                        DeviceAttributeStatus status1 = new DeviceAttributeStatus();
                        status1.setPropCode(item);
                        status1.setPropValue(null);
                        status1.setPropName(keyMap.get(item));
                        collect.add(status1);
                    }
                });
                return collect;
            }
            return deviceRealtimeDataByDevice;
        }
        return null;
    }

    /**
     * 根据设备查当前属性值
     * @param device
     * @return
     */
    private List<DeviceAttributeStatus> getDeviceRealtimeDataByDevice(Device device) {
        List<DeviceAttributeStatus> deviceAttributeStatusList = baseMapper.getDeviceRealtimeDataByDevice(device);
        return getFormatDeviceAttributeStatusList(deviceAttributeStatusList);
    }

    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeDataBigScreen(DeviceAttributeStatus deviceAttributeStatus) {
        if (org.springframework.util.StringUtils.isEmpty(deviceAttributeStatus) || org.springframework.util.StringUtils.isEmpty(deviceAttributeStatus.getTenantId())) {
            return null;
        }
        deviceAttributeStatus.setTenantId(deviceAttributeStatus.getTenantId());
        List<DeviceAttributeStatus> deviceAttributes = getDeviceRealtimeDataGlobal(deviceAttributeStatus);
        //    格式化 设备属性列表，按照约定结构返回

        //   去除 查得的子属性有 对应的父属性却没在 集合中的。如果属性有租户个性化的且复杂类型的父属性isShow 是false 会发生此现象
        Set<String> propCodes = deviceAttributes.stream().map(e -> e.getPropCode()).collect(Collectors.toSet());
        List<DeviceAttributeStatus> deviceAttributeStatusList = new ArrayList<>();
        for (DeviceAttributeStatus deviceAttribute : deviceAttributes) {
            if (ObjectUtils.isNotEmpty(deviceAttribute.getParentPropCode()) && !propCodes.contains(deviceAttribute.getParentPropCode())) {
                continue;
            }
            deviceAttributeStatusList.add(deviceAttribute);
        }
        return getFormatDeviceAttributeStatusList(deviceAttributeStatusList);
    }

    @Override
    public List<DeviceAttributeStatus> getFormatDeviceAttributeStatusList(List<DeviceAttributeStatus> deviceAttributeStatusList) {
        List<String> complexTypesList = Arrays.asList(PhysicalModelTypeConstant.OBJECT, PhysicalModelTypeConstant.ARRAY_SIMPLE, PhysicalModelTypeConstant.ARRAY_OBJECT);
        List<DeviceAttributeStatus> resultList = new ArrayList();
//        查询设备属性状态表 关联属性表

        List<DeviceAttributeStatus> topAttributes = deviceAttributeStatusList.stream().filter(e -> ObjectUtils.isEmpty(e.getParentPropCode())).collect(Collectors.toList());
        for (DeviceAttributeStatus topAttribute : topAttributes) {
//            如果是基本类型的直接添加
            if (!complexTypesList.contains(topAttribute.getUnit())) {
                resultList.add(topAttribute);
            } else if (PhysicalModelTypeConstant.ARRAY_SIMPLE.equals(topAttribute.getUnit())) {
//                arraySimpleList.add(topAttribute);
                resultList.add(topAttribute);
            }
        }

        //    筛选出object  与 array-object的 propCode
        Set<String> parentPropCodes = deviceAttributeStatusList.stream().filter(e -> ObjectUtils.isNotEmpty(e.getParentPropCode())).map(DeviceAttributeStatus::getParentPropCode).collect(Collectors.toSet());

        List<DeviceAttribute> parentDeviceAttrs = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(parentPropCodes)) {
            QueryWrapper<DeviceAttribute> qw = new QueryWrapper();
            qw.in("identifier", parentPropCodes);
//            qw.eq("is_show", true);
            qw.eq("device_unit_id", deviceAttributeStatusList.get(0).getDeviceUnitId());
            parentDeviceAttrs = deviceAttributeMapper.selectList(qw);
        }
        for (DeviceAttribute parentDeviceAttr : parentDeviceAttrs) {

            DeviceAttributeStatus deviceAttrStatus = copyDeviceAttributeStatusFromDeviceAttr(parentDeviceAttr);

            if (PhysicalModelTypeConstant.ARRAY_OBJECT.equals(parentDeviceAttr.getUnit())) {
                //如果是数组对象就只显示顶层的 等点击详情弹框再请求

                Map<Integer, List<DeviceAttributeStatus>> deviceAttributeStatusMap = deviceAttributeStatusList.stream()
                        .filter(e -> parentDeviceAttr.getIdentifier().equals(e.getParentPropCode())).sorted((a, b) -> {
                            if (a.getArrayIndex() != null && b.getArrayIndex() != null && !a.getArrayIndex().equals(b.getArrayIndex())) {
                                return a.getArrayIndex() > b.getArrayIndex() ? 1 : -1;
                            }
                            return 0;
                        }).collect(Collectors.groupingBy(DeviceAttributeStatus::getArrayIndex));

                if (ObjectUtils.isNotEmpty(deviceAttributeStatusMap)) {
                    List<List<DeviceAttributeStatus>> tempValues = deviceAttributeStatusMap.values().stream().collect(Collectors.toList());
                    deviceAttrStatus.setPropValue(JSONObject.toJSONString(tempValues));
                }

                resultList.add(deviceAttrStatus);
            } else if (PhysicalModelTypeConstant.OBJECT.equals(parentDeviceAttr.getUnit())) {
                List<DeviceAttributeStatus> childAttributeList = deviceAttributeStatusList.stream().filter(e -> parentDeviceAttr.getIdentifier().equals(e.getParentPropCode())).sorted((a, b) -> {
                    if (a.getSortNo() != null && b.getSortNo() != null && !a.getSortNo().equals(b.getSortNo())) {
                        return a.getSortNo() > b.getSortNo() ? 1 : -1;
                    }
                    return 0;

                }).collect(Collectors.toList());

                deviceAttrStatus.setChildAttributeList(childAttributeList);
                resultList.add(deviceAttrStatus);
            } else {
                LOGGER.info("出现了不该出现的类型，类型范围超出了array-object,object");
            }
        }
        resultList.sort((a, b) -> {
            if (a.getSortNo() != null && b.getSortNo() != null && !a.getSortNo().equals(b.getSortNo())) {
                return a.getSortNo() > b.getSortNo() ? 1 : -1;
            }
            return 0;
        });
        return resultList;
    }

    private DeviceAttributeStatus copyDeviceAttributeStatusFromDeviceAttr(DeviceAttribute deviceAttribute) {
        DeviceAttributeStatus deviceAttributeStatus = new DeviceAttributeStatus();

        deviceAttributeStatus.setDeviceUnitId(deviceAttribute.getDeviceUnitId());
        deviceAttributeStatus.setPropName(deviceAttribute.getName());
        deviceAttributeStatus.setPropCode(deviceAttribute.getIdentifier());
        deviceAttributeStatus.setUnit(deviceAttribute.getUnit());
        deviceAttributeStatus.setSpecs(deviceAttribute.getSpecs());
        deviceAttributeStatus.setSortNo(deviceAttribute.getSortNo());
        deviceAttributeStatus.setIsShow(deviceAttribute.getIsShow());
        deviceAttributeStatus.setIcoPath(deviceAttribute.getIcoPath());

        return deviceAttributeStatus;
    }


    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeDataByParentPropCode(DeviceAttributeStatus deviceAttributeStatus) {

        Assert.hasLength(deviceAttributeStatus.getParentPropCode(), "父属性code不能为空");
        Assert.hasLength(deviceAttributeStatus.getDeviceUnitId(), "属性code为空");
        deviceAttributeStatus.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        List<DeviceAttributeStatus> deviceAttributes = getDeviceRealtimeDataGlobal(deviceAttributeStatus);

        return deviceAttributes;
    }

    private List<DeviceAttributeStatus> getDeviceRealtimeDataGlobal(DeviceAttributeStatus deviceAttributeStatus) {
        List<DeviceAttributeStatus> deviceAttributes = baseMapper.getDeviceRealtimeDataGlobal(deviceAttributeStatus);
        //   手动过滤 isShow
        if (deviceAttributeStatus.getIsShow() == null) {
            return deviceAttributes;
        }
        deviceAttributes = deviceAttributes.stream().filter(e -> e.getIsShow() || ObjectUtils.isNotEmpty(e.getParentPropCode())).collect(Collectors.toList());

        return deviceAttributes;
    }



    @Override
    public IPage<Map> getHistoricDataFromEs(RequestModel<DeviceAttributeStatus> requestModel) {
        DeviceAttributeStatus queryModel = requestModel.getCustomQueryParams();
        Page page = requestModel.getPage();
//        如果没有勾选要查询的属性，直接返回结果
        if (ObjectUtils.isEmpty(queryModel.getNecessaryFieldList()) && StringUtils.isBlank(queryModel.getTenantId())) {
            return page;
        }
        Assert.notNull(queryModel, "customQueryParams 不能为空");
        Assert.notNull(page, "page 不能为空");
        Long indexLong = page.getCurrent();
        Long sizeLong = page.getSize();

        LinkappTenant currentTenant = null;
        if (StringUtils.isNotBlank(queryModel.getTenantId())){
            currentTenant = linkappTenantService.getOneById(queryModel.getTenantId());
        }else {
            currentTenant = linkappTenantService.currentTenant();
        }

        Assert.notNull(currentTenant, "当前租户为空");
        Assert.notNull(currentTenant.getProjectId(), "当前租户项目id为空");
//        LOGGER.info("定位历史数据查询为空问题，当前租户是：{}", currentTenant);
        Long projectId = CommonUtils.parseLong(currentTenant.getProjectId());

        SearchHits searchHits = realService.getDeviceDataListFromES(queryModel.getDeviceCode(), projectId, indexLong.intValue(), sizeLong.intValue(), queryModel.getQueryTimeStart(), queryModel.getQueryTimeEnd(), queryModel.getNecessaryFieldList(),requestModel.getSorts());
        if (searchHits == null) {
            for (int i = 1; i <= 3; i++) {
                LOGGER.info("定位历史数据查询为空问题，查询searchHits是null,重试第：" + i + " 次");
                try {
                    Thread.sleep(200 * i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                searchHits = realService.getDeviceDataListFromES(queryModel.getDeviceCode(), projectId, indexLong.intValue(), sizeLong.intValue(), queryModel.getQueryTimeStart(), queryModel.getQueryTimeEnd(), queryModel.getNecessaryFieldList(), requestModel.getSorts());
                if (searchHits != null) {
                    break;
                }
            }
            if (searchHits == null) {
                LOGGER.info("定位历史数据查询为空问题，查询searchHits是null,直接返回空结果！");
                return page;
            }
        }
        LOGGER.info("es查询结果到设备历史数据条数 ：" + searchHits.getTotalHits());
        SearchHit[] results = searchHits.getHits();

        List<Map<String, Object>> records = realService.parseResultFromSearchHit(results);

        setSpecsForEsRecords(queryModel, records);
        page.setRecords(records);
        page.setTotal(searchHits.getTotalHits());

        return page;
    }


    @Override
    public Date getLatestRecordTime(RequestModel<DeviceAttributeStatus> requestModel) {
        String deviceCode = requestModel.getCustomQueryParams().getDeviceCode();
        List<String> necessaryFieldList = requestModel.getCustomQueryParams().getNecessaryFieldList();
        //目前的场景传参为 {
        //  "deviceUnitId": "618388c2e33b45cfd18fad7f5a8fde99",
        //  "deviceCode": "27-230928017",
        //  "necessaryFieldList": [
        //    "f1",
        //    "f2",
        //    "f3"
        //  ],
        //  "queryTimeEnd": "2024-04-13 09:27:00",
        //  "queryTimeStart": "2024-03-29 09:27:00"
        //}
        //如果设备code不存在，返回null
        if (StringUtils.isBlank(deviceCode)) {
            return null;
        }
        //如果参数中存在按某些属性查找最近的记录，返回最近的记录的时间
        List<DeviceAttributeStatus> latestDeviceAttributeStatusList = getLatestDeviceAttributeStatusList(deviceCode,necessaryFieldList);
        if (CollectionUtil.isNotEmpty(latestDeviceAttributeStatusList)) {
            DeviceAttributeStatus latestDeviceAttributeStatus = latestDeviceAttributeStatusList.get(0);
            return latestDeviceAttributeStatus.getModifyTime()==null? latestDeviceAttributeStatus.getCreateTime(): latestDeviceAttributeStatus.getModifyTime();
        }
        return null;
    }

    @Override
    public void exportHistoryExcel(DeviceAttributeStatus deviceAttributeStatus, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(deviceAttributeStatus, "Parameter is empty");
        QueryWrapper qw = new QueryWrapper();
        qw.eq("device_unit_id", deviceAttributeStatus.getDeviceUnitId());
        qw.select("identifier", "specs");
        List<DeviceAttribute> deviceAttributes = deviceAttributeService.list(qw);

        //获得属性信息
        DeviceAttribute deviceAttribute = new DeviceAttribute();
        deviceAttribute.setDeviceUnitId(deviceAttributeStatus.getDeviceUnitId());
        deviceAttribute.setLevel(0);
        List<DeviceAttribute> deviceAttributeList = deviceAttributeService.getAll(deviceAttribute);

        //设备的相关信息
        Device device = new Device();
        device.setCode(deviceAttributeStatus.getDeviceCode());
        IPage<Device> deviceIPage = deviceService.selectDevicesPage(new Page(1, 5), device);
        List<Device> deviceList = deviceIPage.getRecords();
        if (null != deviceList && deviceList.size() > 0) {
            device = deviceList.get(0);
        }

        LinkappTenant currentTenant = linkappTenantService.currentTenant();
        Assert.notNull(currentTenant, "tenant is null");
        Assert.notNull(currentTenant.getProjectId(), "related project is null");
        Long projectId = CommonUtils.parseLong(currentTenant.getProjectId());
        SearchHits searchHits = realService.getDeviceDataListFromES(deviceAttributeStatus.getDeviceCode(), projectId, 1, 5, deviceAttributeStatus.getQueryTimeStart(), deviceAttributeStatus.getQueryTimeEnd(), deviceAttributeStatus.getNecessaryFieldList(), null);

        Assert.isTrue(searchHits.getTotalHits() > 0, "data is null");
        //  当前时间段总条数已超1万条，请缩短时间范围后再操作
        Assert.isTrue(searchHits.getTotalHits() <= 10000, "total over 10000");
        SearchHits searchHitsExcel = realService.getDeviceDataListFromES(deviceAttributeStatus.getDeviceCode(), projectId, 1, (int) searchHits.getTotalHits(), deviceAttributeStatus.getQueryTimeStart(), deviceAttributeStatus.getQueryTimeEnd(), deviceAttributeStatus.getNecessaryFieldList(), null);
        Assert.notNull(searchHitsExcel, "data is null");
        SearchHit[] results = searchHitsExcel.getHits();

        List<Map<String, Object>> records = realService.parseResultFromSearchHit(results);

        Map<String, String> deviceAttrMap = deviceAttributes.stream().collect(Collectors.toMap(DeviceAttribute::getIdentifier, DeviceAttribute::getSpecs));

        for (int i = 0; i < records.size(); i++) {
            Map<String, Object> record = records.get(i);

            for (Map.Entry<String, Object> map : record.entrySet()) {
                //枚举类型
                for (DeviceAttribute devi : deviceAttributeList) {
                    if (devi.getIdentifier().equals(map.getKey())) {
                        if ("enum".equals(devi.getUnit())) {
                            String specsStr = devi.getSpecs();
                            JSONObject specsJsonObject = JSONObject.parseObject(specsStr);
                            String attrStr = specsJsonObject.getString(map.getValue().toString());
                            record.put(map.getKey(), attrStr);
                            continue;
                        }

                    }
                }

                String attrJsonStr = deviceAttrMap.get(map.getKey());
                if (null != attrJsonStr) {
                    JSONObject jsonObject = JSONObject.parseObject(attrJsonStr);
                    String attrStr = jsonObject.getString("unit");
                    if (null != attrStr) {
                        String newAttr = map.getValue() + attrStr;
                        record.put(map.getKey(), newAttr);
                    }
                }
            }
            record.put("orderNum", i + 1);
            record.put("time", record.get("createTime"));
        }


        StringBuffer buffer = new StringBuffer();
        buffer.append("序号");
        buffer.append(":");
        buffer.append("orderNum");
        buffer.append(",");
        buffer.append("上报时间");
        buffer.append(":");
        buffer.append("time");
        buffer.append(",");
        for (int i = 0; i < deviceAttributeStatus.getCheckedAttrName().size(); i++) {
            buffer.append(deviceAttributeStatus.getCheckedAttrName().get(i));
            buffer.append(":");
            buffer.append(deviceAttributeStatus.getNecessaryFieldList().get(i));
            if (i != deviceAttributeStatus.getCheckedAttrName().size() - 1) {
                buffer.append(",");
            }

        }
        String keyValue = buffer.toString();

        String title = "设备名称:" + device.getName() + ",设备编号:" + device.getCode() +
                ";设备类型:" + device.getDeviceTypeName() + ",设备型号:" + device.getDeviceUnitCode() +
                ";空间区域:" + device.getSpaceName();
        if (null != device.getAreaName()) {
            title = title + ":" + device.getAreaName();
        }
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportHistoryExcel(outputStream, keyValue, records, ExcelConstant.XLS, title, deviceAttributeStatus.getCheckedAttrName());
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }


    }

    /**
     * 给es 查询结果塞入单位
     */
    public void setSpecsForEsRecords(DeviceAttributeStatus queryModel, List<Map<String, Object>> records) {
        if (ObjectUtils.isEmpty(queryModel.getDeviceUnitId())) {
            return;
        }
        QueryWrapper qw = new QueryWrapper();
        qw.eq("device_unit_id", queryModel.getDeviceUnitId());
        qw.select("identifier", "specs");
        List<DeviceAttribute> deviceAttributes = deviceAttributeService.list(qw);
        if (ObjectUtils.isEmpty(deviceAttributes)) {
            return;
        }
//        去掉identifier 重复的元素
        Set<String> identifierSet = new HashSet();
        LinkedHashSet<DeviceAttribute> deviceAttributeSet = new LinkedHashSet<>();
        for (DeviceAttribute deviceAttribute : deviceAttributes) {
            if (!identifierSet.contains(deviceAttribute.getIdentifier())) {
                deviceAttributeSet.add(deviceAttribute);
            }
            identifierSet.add(deviceAttribute.getIdentifier());
        }

        Map<String, String> deviceAttrMap = deviceAttributeSet.stream().collect(Collectors.toMap(DeviceAttribute::getIdentifier, DeviceAttribute::getSpecs));
        for (int i = 0; i < records.size(); i++) {
            Map<String, Object> record = records.get(i);
            record.put("specsMap", deviceAttrMap);
        }
    }

    @Override
    public List<DeviceAttributeStatus> getLatestDeviceAttributeStatusList(String code, List<String> necessaryFieldList) {
        if (ObjectUtils.isEmpty(code)) {
            return new ArrayList<>();
        }
        QueryWrapper<DeviceAttributeStatus> qw = new QueryWrapper<>();
        qw.eq("device_code", code);
        qw.isNotNull("version");
        if (CollectionUtil.isNotEmpty(necessaryFieldList)){
            qw.in("prop_code", necessaryFieldList);
        }
        qw.orderByDesc("modify_time", "create_time");
        List<DeviceAttributeStatus> deviceAttributeStatusList = list(qw);

        List<DeviceAttributeStatus> deviceAttributeStatusList2 = new ArrayList<>();

// 查到之后取最新一条
        Set<String> propCodeSet = new HashSet<>();

        for (DeviceAttributeStatus deviceAttributeStatus : deviceAttributeStatusList) {
            if (propCodeSet.contains(deviceAttributeStatus.getPropCode())) {
                continue;
            }
            deviceAttributeStatusList2.add(deviceAttributeStatus);
            propCodeSet.add(deviceAttributeStatus.getPropCode());
        }

        return deviceAttributeStatusList2;
    }

    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeDataNoVersion(DeviceAttributeStatus deviceAttributeStatus) {
        if (deviceAttributeStatus == null || deviceAttributeStatus.getDeviceCode() == null) {
            return null;
        }
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        Device device = new Device();
        device.setTenantId(tenantId);
        device.setCode(deviceAttributeStatus.getDeviceCode());
        List<Device> deviceList = deviceMapper.selectDevices(device);
        if (deviceList == null || deviceList.size() <= 0) {
            return null;
        }
        device = deviceList.get(0);
        deviceAttributeStatus.setTenantId(tenantId);
        deviceAttributeStatus.setVersion(device.getDeviceUnitVersion());
        List<DeviceAttributeStatus> deviceAttributes = getDeviceRealtimeDataGlobal(deviceAttributeStatus);
        //    格式化 设备属性列表，按照约定结构返回

        //   去除 查得的子属性有 对应的父属性却没在 集合中的。如果属性有租户个性化的且复杂类型的父属性isShow 是false 会发生此现象
        Set<String> propCodes = deviceAttributes.stream().map(e -> e.getPropCode()).collect(Collectors.toSet());
        List<DeviceAttributeStatus> deviceAttributeStatusList = new ArrayList<>();
        for (DeviceAttributeStatus deviceAttribute : deviceAttributes) {
            if (ObjectUtils.isNotEmpty(deviceAttribute.getParentPropCode()) && !propCodes.contains(deviceAttribute.getParentPropCode())) {
                continue;
            }
            deviceAttributeStatusList.add(deviceAttribute);
        }

        return getFormatDeviceAttributeStatusList(deviceAttributeStatusList);
    }


    @Override
    public boolean saveOrUpdate2(DeviceAttributeStatus deviceAttributeStatus) {
        try {
            deviceAttributeStatus.setUniqueTag();
            LOGGER.info("saveOrUpdate2保存设备属性状态：{}", deviceAttributeStatus);
            return save(deviceAttributeStatus);
        } catch (DuplicateKeyException e) {
            LOGGER.info("新增设备最新属性监测到有重复转而修改：{}", deviceAttributeStatus);
            return update2(deviceAttributeStatus);
        } catch (Exception e) {
            LOGGER.error("新增或修改设备最新属性捕获到了其他的异常:", e);
            return false;
        }
    }

    @Override
    public boolean update2(DeviceAttributeStatus deviceAttributeStatus) {
        String lockKey = deviceAttributeStatus.getDeviceCode() + deviceAttributeStatus.getVersion() + deviceAttributeStatus.getPropCode();
        RLock redissonLock = redissonClient.getLock("deviceAttributeStatus_update_" + lockKey);
        if (redissonLock.tryLock()) {
            try {
                deviceAttributeStatus.setUniqueTag();
                QueryWrapper<DeviceAttributeStatus> qw = new QueryWrapper<>();
                qw.eq("device_code", deviceAttributeStatus.getDeviceCode());
                qw.eq("prop_code", deviceAttributeStatus.getPropCode());
                qw.eq("version", deviceAttributeStatus.getVersion());
                if (deviceAttributeStatus.getParentPropCode() != null) {
                    qw.eq("parent_prop_code", deviceAttributeStatus.getParentPropCode());
                }
                if (deviceAttributeStatus.getArrayIndex() != null) {
                    qw.eq("array_index", deviceAttributeStatus.getArrayIndex());
                }

                List<DeviceAttributeStatus> list = list(qw);
                if (list.size() == 0) {
                    LOGGER.info("update2保存设备属性状态：{}", deviceAttributeStatus);
                    return save(deviceAttributeStatus);
                } else {
//            更新 设备属性状态
                    DeviceAttributeStatus das0 = list.get(0);
                    if ((das0.getTimestamp() != null && deviceAttributeStatus.getTimestamp() != null)
                            && das0.getTimestamp() > deviceAttributeStatus.getTimestamp()) {
                        LOGGER.info("时间戳不是最新的，不予更新：{}", deviceAttributeStatus);
                        return false;
                    }
                    deviceAttributeStatus.setId(das0.getId());
                    LOGGER.info("修改设备属性状态：{}", deviceAttributeStatus);
                    return updateById(deviceAttributeStatus);
                }
            } catch (Exception e) {
                LOGGER.error("update2发生错误", e);
                return false;
            } finally {
                if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
                    redissonLock.unlock();
                }
            }
        } else {
            LOGGER.info("获取锁'deviceAttributeStatus_update_" + lockKey + "'失败");
            return false;
        }
    }

    /**
     * 环境管理的设备，创建时间最新
     * @param deviceAttributeStatus
     * @param deviceTypeName 设备类型名称
     * @return
     */
    @Override
    public List<DeviceAttributeStatus> getDeviceRealtimeDataByCertainTag(DeviceAttributeStatus deviceAttributeStatus, String deviceTypeName) {
        if (ObjectUtils.isEmpty(deviceTypeName)) {
            return null;
        }
        LambdaQueryWrapper<DeviceUnit> unitLambdaQueryWrapper = new LambdaQueryWrapper<>();
        unitLambdaQueryWrapper.eq(DeviceUnit::getDeviceTypeName, deviceTypeName).select(DeviceUnit::getId,DeviceUnit::getVersion);
        List<DeviceUnit> deviceUnits = deviceUnitService.list(unitLambdaQueryWrapper);
        if (ObjectUtils.isNotEmpty(deviceUnits)) {
            LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.in(Device::getDeviceUnitId, deviceUnits.stream().map(DeviceUnit::getId).collect(Collectors.toList())).eq(Device::getDeleteState, 1).select(Device::getCode,Device::getDeviceUnitId,Device::getTenantId).orderByDesc(Device::getCreateTime);
            List<Device> devices = deviceService.list(lambdaQueryWrapper);
            if (ObjectUtils.isEmpty(devices)) {
                return null;
            }
            //如果传过来有tenantId,则进行过滤
            if(StringUtils.isNotBlank(deviceAttributeStatus.getTenantId())){
                devices = devices.stream().filter(item->item.getTenantId().equals(deviceAttributeStatus.getTenantId())).collect(Collectors.toList());
                if (ObjectUtils.isEmpty(devices)) {
                    return null;
                }
            }
            List<String> deviceCodes = devices.stream().map(Device::getCode).collect(Collectors.toList());
            LambdaQueryWrapper<DeviceModel> deviceModelLambdaQueryWrapper = new LambdaQueryWrapper<>();
            deviceModelLambdaQueryWrapper.in(DeviceModel::getCode, deviceCodes).select(DeviceModel::getCode);
            List<DeviceModel> deviceModels = deviceModelMapper.selectList(deviceModelLambdaQueryWrapper);
            if (ObjectUtils.isEmpty(deviceModels)) {
                return null;
            }
            List<String> deviceModelCodes = deviceModels.stream().map(DeviceModel::getCode).collect(Collectors.toList());
            for (Device device : devices) {
                if (deviceModelCodes.contains(device.getCode())) {
                    deviceAttributeStatus.setDeviceCode(device.getCode());
                    DeviceUnit deviceUnit = deviceUnits.stream().filter(e -> e.getId().equals(device.getDeviceUnitId())).findFirst().get();
                    if (deviceUnit != null) {
                        deviceAttributeStatus.setVersion(deviceUnit.getVersion());
                    }
                    break;
                }
            }
            Assert.notNull(deviceAttributeStatus.getDeviceCode(), "根据参数查得设备编号为空");
            return getDeviceRealtimeData(deviceAttributeStatus);
        }
        return null;
    }

    /**
     * 通过项目编码获取设备状态数据
     * @param map
     * @return
     */
    @Override
    public List<DeviceAttributeStatus> getDeviceDataByProject(Map<String, String> map) {

        // 通过设备编码查询设备当前版本号
        String version = baseMapper.queryDeviceVersion(map.get("deviceCode"));

        // 通过城建项目编号查询施工云项目租户ID
        QueryWrapper<LinkappTenant> qw = new QueryWrapper<>();
        qw.eq("project_code_", map.get("projectCode"));
        String tenantId = linkappTenantMapper.selectOne(qw).getId();

        DeviceAttributeStatus deviceAttributeStatus = new DeviceAttributeStatus();
        deviceAttributeStatus.setDeviceCode(map.get("deviceCode"));
        deviceAttributeStatus.setVersion(version);
        deviceAttributeStatus.setTenantId(tenantId);
        deviceAttributeStatus.setIsShow(true);
        LinkappUser linkappUser = new LinkappUser();
        linkappUser.setTenantId(tenantId);


        List<DeviceAttributeStatus> deviceAttributes = getDeviceRealtimeDataGlobal(deviceAttributeStatus);
        //    格式化 设备属性列表，按照约定结构返回

        //   去除 查得的子属性有 对应的父属性却没在 集合中的。如果属性有租户个性化的且复杂类型的父属性isShow 是false 会发生此现象
        Set<String> propCodes = deviceAttributes.stream().map(e -> e.getPropCode()).collect(Collectors.toSet());
        List<DeviceAttributeStatus> deviceAttributeStatusList = new ArrayList<>();
        for (DeviceAttributeStatus deviceAttribute : deviceAttributes) {
            if (ObjectUtils.isNotEmpty(deviceAttribute.getParentPropCode()) && !propCodes.contains(deviceAttribute.getParentPropCode())) {
                continue;
            }
            deviceAttributeStatusList.add(deviceAttribute);
        }
        List<DeviceAttributeStatus> formatDeviceAttributeStatusList = getFormatDeviceAttributeStatusList(deviceAttributeStatusList);
        //20221011增加没有设备编码、无数据也展示属性：只在设备上报数据为空的时候走新增逻辑
        Boolean noDataShow = deviceAttributeStatus.getNoDataShow();
        if(noDataShow != null && noDataShow){
            if(CollectionUtil.isEmpty(formatDeviceAttributeStatusList)){
                formatDeviceAttributeStatusList = new ArrayList<>();
            }
            noDataShowAttribute(deviceAttributeStatus,formatDeviceAttributeStatusList,linkappUser);
        }

        return formatDeviceAttributeStatusList;
    }
}


